/**
 * \file String.cpp
 * \brief String declaration class
 * \author Edouard ROGE
 * \date 19 september 2010
 */

// to disable warnings from windows compilation
#define  _CRT_SECURE_NO_WARNINGS

#include <stdarg.h>
#include <stdio.h>
#include <iostream>
#include <cstring>
#include "Core/Memory/MemoryPoolManager.h"
#include "String.h"

String::String()
: m_Buffer(NULL)
{
}

String::String( const String & _str)
: m_Buffer(NULL)
{
	Set(_str.m_Buffer);
}

String::String(char * s)
: m_Buffer(NULL)
{
	Set(s);
}

String::String(const char * s)
: m_Buffer(NULL)
{
	Set(s);
}

String::String( char c )
: m_Buffer(NULL)
{
	Set(c);
}

String::~String()
{
	Clear();
}

void String::Append(char c)
{
	if( m_Buffer )
	{
		uint32 iLength = GetLength();
		uint32 iNewLength = iLength + 1;
		char * newBuffer = (char*)MemoryPoolManager::GetInstance().Allocate(sizeof(char) * (iNewLength+1));
		memcpy(newBuffer, m_Buffer, iLength);
		newBuffer[iLength] = c;
		newBuffer[iNewLength] = '\0';
		MemoryPoolManager::GetInstance().Free(m_Buffer);
		m_Buffer = newBuffer;
	}
	else
	{
		Set(c);
	}
}

void String::Append(char * s)
{
	if( m_Buffer )
	{
		uint32 iLength = GetLength();
		uint32 iNewLength = iLength + strlen(s);
		char * newBuffer = (char*)MemoryPoolManager::GetInstance().Allocate(sizeof(char) * (iNewLength+1));
		memcpy(newBuffer, m_Buffer, iLength);
		memcpy(newBuffer+iLength, s, iNewLength - iLength);
		newBuffer[iNewLength] = '\0';
		MemoryPoolManager::GetInstance().Free(m_Buffer);
		m_Buffer = newBuffer;
	}
	else
	{
		Set(s);
	}
}

void String::Append(const char * s)
{
	if( m_Buffer )
	{
		uint32 iLength = GetLength();
		uint32 iNewLength = iLength + strlen(s);
		char * newBuffer = (char*)MemoryPoolManager::GetInstance().Allocate(sizeof(char) * (iNewLength+1));
		memcpy(newBuffer, m_Buffer, iLength);
		memcpy(newBuffer+iLength, s, iNewLength - iLength);
		newBuffer[iNewLength] = '\0';
		MemoryPoolManager::GetInstance().Free(m_Buffer);
		m_Buffer = newBuffer;
	}
	else
	{
		Set(s);
	}
}

void String::Set(char c)
{
	if( m_Buffer )
		MemoryPoolManager::GetInstance().Free( m_Buffer );

	m_Buffer = (char*)MemoryPoolManager::GetInstance().Allocate(sizeof(char) * 2);
	m_Buffer[0] = c;
	m_Buffer[1] = '\0';
}

void String::Set(char * s)
{
	if( m_Buffer )
		MemoryPoolManager::GetInstance().Free( m_Buffer );

	if( s )
	{
		int iLength(strlen(s));
		m_Buffer = (char*)MemoryPoolManager::GetInstance().Allocate(sizeof(char) * (iLength+1));
		memcpy(m_Buffer, s, iLength);
		m_Buffer[iLength] = '\0';
	}
}

void String::Set(const char * s)
{
	if( m_Buffer )
		MemoryPoolManager::GetInstance().Free(m_Buffer);

	if( s )
	{
		int iLength(strlen(s));
		m_Buffer = (char*)MemoryPoolManager::GetInstance().Allocate(sizeof(char) * (iLength+1));
		memcpy(m_Buffer, s, iLength);
		m_Buffer[iLength] = '\0';
	}
}

uint32 String::GetLength() const
{
	return ( m_Buffer ? strlen(m_Buffer) : 0 );
}

String String::SubString(uint32 _iDeb) const
{
	return SubString(_iDeb, GetLength());
}

String String::SubString(uint32 _iDeb, uint32 _iFin) const
{
	String res;
	if( _iDeb >= _iFin )
		return res;
	res.m_Buffer = (char*)MemoryPoolManager::GetInstance().Allocate(sizeof(char) * (_iFin - _iDeb + 1));
	memcpy(res.m_Buffer, m_Buffer + _iDeb, _iFin - _iDeb);
	res.m_Buffer[_iFin - _iDeb] = '\0';
	return res;
}

int String::FindFirst( const String & _subString, uint32 _firstIndex ) const
{
	for(uint32 i = _firstIndex; i < GetLength() - _subString.GetLength(); ++i)
	{
		if( m_Buffer[i] == _subString.m_Buffer[0] )
		{
			uint32 j = 0;
			while( m_Buffer[i+j] == _subString.m_Buffer[j] )
			{
				if( ++j == _subString.GetLength() )
					return i;
			}
		}
	}

	return -1;
}

int String::FindLast( const String & _subString ) const
{
	int iLength = GetLength() - _subString.GetLength() - 1;
	for(int i = iLength; i >=0 ; --i)
	{
		if( m_Buffer[i] == _subString.m_Buffer[0] )
		{
			uint32 j = _subString.GetLength()-1;
			while( m_Buffer[i+j] == _subString.m_Buffer[j] )
			{
				if( --j == 0 )
					return i;
			}
		}
	}

	return -1;
}

int String::FindFirst( char _c, uint32 _firstIndex ) const
{
	for(uint32 i = _firstIndex; i < GetLength(); ++i)
		if( m_Buffer[i] == _c )
			return i;

	return -1;
}

int String::FindLast( char _c ) const
{
	for(int i = GetLength() - 1; i >= 0; --i)
		if( m_Buffer[i] == _c )
			return i;

	return -1;
}

void String::Trim()
{
	if(!m_Buffer)
		return;

	uint32 index = 0;
	while(m_Buffer[index]!='\0')
	{
		if(m_Buffer[index]==' ' || m_Buffer[index]=='\t' || m_Buffer[index]=='\n')
		{
			for(uint32 i=index; i<GetLength(); ++i)
			{
				m_Buffer[i] = m_Buffer[i+1];
			}
		}
		else
		{
			++index;
		}
	}
}

void String::Printf(const char * _fmt, ...)
{	
	va_list ap;
	va_start(ap, _fmt);

	char buf[256];
	vsprintf(buf, _fmt, ap);

	va_end(ap);

	Set(buf);
}

int32 String::ToInt() const
{
	return atoi(m_Buffer);
}

double String::ToDouble() const
{
	return atof(m_Buffer);
}

String::operator const char * () const
{
	return m_Buffer;
}

String & String::operator+= (char c)
{
	Append( c );
	return (*this);
}

String & String::operator+= (char * s)
{
	Append( s );
	return (*this);
}

String & String::operator+= (const char * s)
{
	Append( s );
	return (*this);
}

String & String::operator+= (const String & str)
{
	Append( str.m_Buffer );
	return (*this);
}

String & String::operator= (char c)
{
	Set(c);
	return (*this);
}

String & String::operator= (char * s)
{
	Set(s);
	return (*this);
}

String & String::operator= (const char * s)
{
	Set(s);
	return (*this);
}

String & String::operator= (const String & str)
{
	Set(str.m_Buffer);
	return (*this);
}

/**
 * \fn String operator+ (const String & str, char c);
 * \param str String
 * \param c char
 * \return new string with the 2 params concatenated
 */
String operator+ (const String & str, char c)
{
	String res(str);
	res += c;
	return res;
}

/**
 * \fn String operator+ (const String & str, char *s);
 * \param str string1
 * \param s string2
 * \return new string with the 2 params concatenated
 */
String operator+ (const String & str, char * s)
{
	String res(str);
	if(s)
		res += s;
	return res;
}

/**
 * \fn String operator+ (const String & str, const char *s);
 * \param str string1
 * \param s string2
 * \return new string with the 2 params concatenated
 */
String operator+ (const String & str, const char * s)
{
	String res(str);
	if(s)
		res += s;
	return res;
}

/**
 * \fn String operator+ (char c, const String & str);
 * \param c char
 * \param str string
 * \return new string with the 2 params concatenated
 */
String operator+ (char c, const String & str)
{
	String res = c;
	res += str.m_Buffer;
	return res;
}

/**
 * \fn String operator+ (char * s, const String & str);
 * \param s string1
 * \param str string2
 * \return new string with the 2 params concatenated
 */
String operator+ (char * s, const String & str)
{
	String res(s);
	res += str.m_Buffer;
	return res;
}

/**
 * \fn String operator+ (const char * s, const String & str);
 * \param s string1
 * \param str string2
 * \return new string with the 2 params concatenated
 */
String operator+ (const char * s, const String & str)
{
	String res(s);
	res += str.m_Buffer;
	return res;
}

/**
 * \fn String operator+ (const String & str1, const String & str2);
 * \param str1 string1
 * \param str2 string2
 * \return new string with the 2 params concatenated
 */
String operator+ (const String & str1, const String & str2)
{
	String res(str1);
	res += str2.m_Buffer;
	return res;
}

char & String::operator[](uint32 i)
{
	//LOG_ASSERT( (i < GetLength()), "Index out of bounds");
	return m_Buffer[i];
}

char String::operator[](uint32 i) const
{
	//LOG_ASSERT( (i < GetLength()), "Index out of bounds");
	return m_Buffer[i];
}

char * String::GetData() const
{
	return m_Buffer;
}

void String::Replace( char _cOldChar, char _cNewChar )
{
	for(uint32 i = 0; i < GetLength(); ++i)
		if( m_Buffer[i] == _cOldChar )
			m_Buffer[i] = _cNewChar;
}

void String::Reserve( uint32 _iSize )
{
	Clear();
	m_Buffer = (char*)MemoryPoolManager::GetInstance().Allocate(sizeof(char) * _iSize);
	m_Buffer[_iSize-1] = '\0';
}

void String::Clear()
{
	if( m_Buffer )
	{
		MemoryPoolManager::GetInstance().Free( m_Buffer );
		m_Buffer = NULL;
	}
}

/**
 * \fn bool operator==(const String & str1, const String & str2);
 * \param str1 first string to compare
 * \param str2 first string to compare
 * \return true if the two string are equals
 * comparison operator
 */
bool operator==(const String & str1, const String & str2)
{
	uint32 iLength = str1.GetLength();
	if( iLength != str2.GetLength() )
		return false;

	for(uint32 i = 0; i < iLength; ++i)
		if( str1[i] != str2[i] )
			return false;

	return true;
}

/**
 * \fn bool operator==(const String & str1, const char * str2);
 * \param str1 first string to compare
 * \param str2 first string to compare
 * \return true if the two string are equals
 * comparison operator
 */
bool operator==(const String & str1, const char * str2)
{
	uint32 iLength1 = str1.GetLength();
	uint32 iLength2 = str2 ? strlen(str2) : 0;
	if( iLength1 != iLength2 )
		return false;

	for(uint32 i = 0; i < iLength1; ++i)
		if( str1[i] != str2[i] )
			return false;

	return true;
}

/**
 * \fn bool operator==(const char * str1, const String & str2);
 * \param str1 first string to compare
 * \param str2 first string to compare
 * \return true if the two string are equals
 * comparison operator
 */
bool operator==(const char * str1, const String & str2)
{
	return (str2==str1);
}

/**
 * \fn bool operator!=(const String & str1, const String & str2);
 * \param str1 first string to compare
 * \param str2 first string to compare
 * \return true if the two string are non equals
 * comparison operator
 */
bool operator!=(const String & str1, const String & str2)
{
	return !( str1 == str2 );
}

/**
 * \fn bool operator!=(const String & str1, const char * str2);
 * \param str1 first string to compare
 * \param str2 first string to compare
 * \return true if the two string are non equals
 * comparison operator
 */
bool operator!=(const String & str1, const char * str2)
{
	return !( str1 == str2 );
}

/**
 * \fn bool operator!=(const char * str1, const String & str2);
 * \param str1 first string to compare
 * \param str2 first string to compare
 * \return true if the two string are non equals
 * comparison operator
 */
bool operator!=(const char * str1, const String & str2)
{
	return (str2!=str1);
} 
